#include "stdafx.h"
#include "objectloader.h"

CObjectLoader::CObjectLoader(void)
{
}

CObjectLoader::~CObjectLoader(void)
{
    logi("CObjectLoader::~CObjectLoader");
}

HRESULT CObjectLoader::Load(LPSTRING path, LPCSTR name)
{
    rc_error(path != NULL, E_FAIL);
    rc_error(fileisexist(path) == S_SUCCESS, E_FAIL);

    HRESULT hr = S_OK;

    SYNC_OBJ(&m_lock)

    rc_error(m_mapContainer.count(name) == 0, S_OK);

    CComLoader& loader = m_mapContainer[name];

    hr = loader.LoadCom(path);
    rc_error(hr == S_OK, E_FAIL);

    hr = loader.DllStart();
    rc_error(hr == S_OK, E_FAIL);

    LONG count = loader.GetCount();
    rc_error(count > 0, E_FAIL);

    LONG loop = 0;
    for (loop = 0; loop < count; loop++) {
        CLSID clsid = loader.GetAt(loop);
        LPCSTR lpProgId = loader.ProgIDFromCLSID(clsid);

        rc_error_continue(lpProgId && *lpProgId);
        rc_error_continue(m_mapCls.count(clsid) == 0);

        ClsInfo& cls = m_mapCls[clsid];
        cls.pname = name;
        cls.progid = lpProgId;
    }

    return S_OK;
}

HRESULT CObjectLoader::Remove(LPCSTR name)
{
    SYNC_OBJ(&m_lock)
    rc_error(m_mapContainer.count(name), E_FAIL);

    ContainerTableiterator it = m_mapContainer.find(name);

    if (it->second.DllStop) {
        it->second.DllStop();
    }

    m_mapContainer.erase(name);

    return S_OK;
}
HRESULT CObjectLoader::Free()
{
    SYNC_OBJ(&m_lock)

    ContainerTableiterator it = m_mapContainer.begin();
    while (it != m_mapContainer.end()) {
        if (it->second.DllStop)
            it->second.DllStop();
        it++;
    }

    m_mapContainer.clear();
    return S_OK;
}

DWORD CObjectLoader::GetCount()
{
    SYNC_OBJ(&m_lock)
    return m_mapContainer.size();
}
HRESULT CObjectLoader::Get(LPCSTR name, basic_tchar* path, int len)
{
    rc_error(name != NULL, E_FAIL);
    rc_error(path != NULL, E_FAIL);
    rc_error(len > 0, E_FAIL);

    ContainerTableiterator it = m_mapContainer.find(name);
    rc_error((m_mapContainer.end() != it), E_FAIL);

    tstring_strcpy(path, len, it->second.GetComPath());

    return S_OK;
}

HRESULT CObjectLoader::Register(SafeStringConstPtr name, UINT type, UINT version)
{
    HRESULT hr = S_OK;

    rc_error(name != NULL, E_FAIL);
    rc_error(name->ptr != NULL, E_FAIL);
    rc_error(name->len != 0, E_FAIL);

    SYNC_OBJ(&m_lock)

    ContainerTableiterator it = m_mapContainer.find(name->ptr);
    rc_error((m_mapContainer.end() != it), E_FAIL);

    it->second.Setup(type, version);

    return hr;
}
HRESULT CObjectLoader::UnRegister(SafeStringConstPtr name, UINT type, UINT version)
{
    HRESULT hr = S_OK;

    rc_error(name != NULL, E_FAIL);
    rc_error(name->ptr != NULL, E_FAIL);
    rc_error(name->len != 0, E_FAIL);

    SYNC_OBJ(&m_lock)

    ContainerTableiterator it = m_mapContainer.find(name->ptr);
    rc_error((m_mapContainer.end() != it), E_FAIL);

    it->second.Setup(type, version);

    return hr;
}
HRESULT CObjectLoader::Registers(UINT type, UINT version)
{
    SYNC_OBJ(&m_lock)

    ContainerTableiterator it = m_mapContainer.begin();
    rc_error((m_mapContainer.end() != it), E_FAIL);
    while (it != m_mapContainer.end()) {
        it->second.Setup(type, version);
        it++;
    }

    return S_OK;
}
HRESULT CObjectLoader::UnRegisters(UINT type, UINT version)
{
    SYNC_OBJ(&m_lock)
    ContainerTableiterator it = m_mapContainer.begin();
    rc_error((m_mapContainer.end() != it), E_FAIL);
    while (it != m_mapContainer.end()) {
        it->second.Setup(type, version);
        it++;
    }

    return S_OK;
}

HRESULT CObjectLoader::CreateInstance(REFCLSID clsid, IBase* punk, REFIID iid, void** ppv)
{
    HRESULT hr = S_OK;
    SYNC_OBJ(&m_lock)

    // m_mapCls.end() == cls no_found == > false
    // m_mapCls.end() != cls found == > true
    // rc_error(!false)

    /*
    How do you create an instance of a COM component whose class name is generally unknown to
    external consumers of the component ground? Who will create it? The COM specification states
    that each component must implement a corresponding Class Factory. The class factory is also a
    COM component that implements the IClassFactory interface

    I think it's a defensive device. Prevent objects from being created randomly --com
    */

    ClsIdTableConstiterator cls = m_mapCls.find(clsid);
    rc_error(m_mapCls.end() != cls, E_INVALIDARG);

    ContainerTableConstiterator it = m_mapContainer.find(cls->second.pname);
    rc_error((m_mapContainer.end() != it), E_INVALIDARG);

    ClsFactoryPtr pfac;

    hr = it->second.DllGetClassObject(clsid, IID_IClsFactory, (void**)&pfac);

    rc_error(hr == S_OK, E_FAIL);
    rc_error(pfac.m_p != NULL, E_FAIL);

    return pfac->CreateInstance(punk, iid, ppv);
}

LPCSTR CObjectLoader::ProgIDFromCLSID(REFCLSID clsid)
{
    SYNC_OBJ(&m_lock)
    ClsIdTableConstiterator it = m_mapCls.find(clsid);
    rc_error((m_mapCls.end() != it), "");
    return it->second.progid.c_str();
}

CLSID CObjectLoader::CLSIDFromProgID(LPCSTR progid)
{
    rc_error(progid && *progid, IID_INULL);
    SYNC_OBJ(&m_lock)

    size_t sSize = m_mapCls.size();
    rc_error(sSize > 0, IID_INULL);

    ClsIdTableiterator it = m_mapCls.begin();
    for (; it != m_mapCls.end(); it++) {
        ClsInfo& cls = it->second;
        if (0 == string_stricmp(cls.progid.c_str(), progid)) {
            return it->first;
        }
    }
    return IID_INULL;
}
